package com.weifly.weistock.record.bill.impl;

import com.weifly.weistock.core.common.StockException;
import com.weifly.weistock.record.base.AbstractStoreService;
import com.weifly.weistock.record.base.domain.RecordPair;
import com.weifly.weistock.record.bill.BillConst;
import com.weifly.weistock.record.bill.BillStoreService;
import com.weifly.weistock.record.bill.BillUtils;
import com.weifly.weistock.record.bill.domain.StockRecordDto;
import com.weifly.weistock.record.bill.domain.StockSummaryDto;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 股票交易记录存储服务
 *
 * @author weifly
 * @since 2020/01/16
 */
public class BillStoreServiceImpl extends AbstractStoreService implements BillStoreService {

    private Logger log = LoggerFactory.getLogger(BillStoreServiceImpl.class);

    private static final String ELE_ROOT = "root";
    private static final String ELE_RECORD = "record";
    private static final String ATTR_STOCK_CODE = "stockCode";
    private static final String ATTR_STOCK_NAME = "stockName";
    private static final String ATTR_DATE = "date";
    private static final String ATTR_TIME = "time";
    private static final String ATTR_BUSINESS_NAME = "businessName";
    private static final String ATTR_TRADE_PRICE = "tradePrice";
    private static final String ATTR_TRADE_NUMBER = "tradeNumber";
    private static final String ATTR_TRADE_AMOUNT = "tradeAmount";
    private static final String ATTR_AFTER_NUMBER = "afterNumber";
    private static final String ATTR_FEE_SERVICE = "feeService";
    private static final String ATTR_FEE_STAMP = "feeStamp";
    private static final String ATTR_FEE_TRANSFER = "feeTransfer";
    private static final String ATTR_FEE_EXTRA = "feeExtra";
    private static final String ATTR_FEE_CLEAR = "feeClear";
    private static final String ATTR_CLEAR_AMOUNT = "clearAmount";
    private static final String ATTR_AFTER_AMOUNT = "afterAmount";
    private static final String ATTR_ENTRUST_CODE = "entrustCode";
    private static final String ATTR_PAIR = "pair";
    private static final String ATTR_DIFF_AMOUNT = "diffAmount";

    @Override
    public StockSummaryDto loadStockSummary(String stockCode) {
        File storeFolder = this.getStoreFile();
        String xmlFileName = "bill_" + stockCode + ".xml";
        File xmlFile = new File(storeFolder, xmlFileName);
        if(!xmlFile.exists()){
            log.info("文件不存在：" + xmlFile.getAbsolutePath());
            return null;
        }

        log.info("加载股票记录：" + xmlFile.getAbsolutePath());
        return this.loadXmlFile(xmlFile);
    }

    private StockSummaryDto loadXmlFile(File xmlFile){
        StockSummaryDto summaryDto = new StockSummaryDto();
        try{
            SAXReader reader = new SAXReader();
            Document document = reader.read(xmlFile);
            Element rootEle = document.getRootElement();

            String stockCode = rootEle.attributeValue(ATTR_STOCK_CODE);
            summaryDto.setStockCode(stockCode);
            String stockName = rootEle.attributeValue(ATTR_STOCK_NAME);
            summaryDto.setStockName(stockName);
            String summaryDiffAmount = rootEle.attributeValue(ATTR_DIFF_AMOUNT);
            if(StringUtils.isNotBlank(summaryDiffAmount)){
                summaryDto.setDiffAmount(Double.valueOf(summaryDiffAmount));
            }
            String summaryFeeService = rootEle.attributeValue(ATTR_FEE_SERVICE);
            if(StringUtils.isNotBlank(summaryFeeService)){
                summaryDto.setFeeService(Double.valueOf(summaryFeeService));
            }
            String summaryFeeStamp = rootEle.attributeValue(ATTR_FEE_STAMP);
            if(StringUtils.isNotBlank(summaryFeeStamp)){
                summaryDto.setFeeStamp(Double.valueOf(summaryFeeStamp));
            }
            String summaryAfterNumber = rootEle.attributeValue(ATTR_AFTER_NUMBER);
            if(StringUtils.isNotBlank(summaryAfterNumber)){
                summaryDto.setAfterNumber(Integer.valueOf(summaryAfterNumber));
            }

            List<Element> recordEleList = rootEle.elements(ELE_RECORD);
            if(recordEleList!=null){
                for(Element recordEle : recordEleList){
                    StockRecordDto recordDto = new StockRecordDto();
                    // 发生日期
                    recordDto.setDate(recordEle.attributeValue(ATTR_DATE));
                    // 成交时间
                    recordDto.setTime(recordEle.attributeValue(ATTR_TIME));
                    // 业务名称
                    String businessNameValue = recordEle.attributeValue(ATTR_BUSINESS_NAME);
                    BillConst.BusinessName businessNameEnum = BillConst.BusinessName.getEnumByName(businessNameValue);
                    if(businessNameEnum==null){
                        throw new StockException("不支持的businessName: " + businessNameValue);
                    }
                    recordDto.setBusinessName(businessNameEnum);
                    // 证券代码
                    recordDto.setStockCode(recordEle.attributeValue(ATTR_STOCK_CODE));
                    // 证券名称
                    recordDto.setStockName(recordEle.attributeValue(ATTR_STOCK_NAME));
                    // 成交价格
                    recordDto.setTradePrice(Double.valueOf(recordEle.attributeValue(ATTR_TRADE_PRICE)));
                    // 成交数量
                    recordDto.setTradeNumber(Integer.valueOf(recordEle.attributeValue(ATTR_TRADE_NUMBER)));
                    // 成交金额
                    recordDto.setTradeAmount(Double.valueOf(recordEle.attributeValue(ATTR_TRADE_AMOUNT)));
                    // 股份余额
                    recordDto.setAfterNumber(Integer.valueOf(recordEle.attributeValue(ATTR_AFTER_NUMBER)));
                    // 手续费
                    recordDto.setFeeService(Double.valueOf(recordEle.attributeValue(ATTR_FEE_SERVICE)));
                    // 印花税
                    recordDto.setFeeStamp(Double.valueOf(recordEle.attributeValue(ATTR_FEE_STAMP)));
                    // 过户费
                    recordDto.setFeeTransfer(Double.valueOf(recordEle.attributeValue(ATTR_FEE_TRANSFER)));
                    // 附加费
                    recordDto.setFeeExtra(Double.valueOf(recordEle.attributeValue(ATTR_FEE_EXTRA)));
                    // 交易所清算费
                    recordDto.setFeeClear(Double.valueOf(recordEle.attributeValue(ATTR_FEE_CLEAR)));
                    // 发生金额
                    recordDto.setClearAmount(Double.valueOf(recordEle.attributeValue(ATTR_CLEAR_AMOUNT)));
                    // 资金本次余额
                    recordDto.setAfterAmount(Double.valueOf(recordEle.attributeValue(ATTR_AFTER_AMOUNT)));
                    // 委托编号
                    recordDto.setEntrustCode(recordEle.attributeValue(ATTR_ENTRUST_CODE));

                    // 配对信息
                    List<RecordPair> pairList = this.parsePairAttr(recordEle.attributeValue(ATTR_PAIR), recordDto.getTradeNumber());
                    if(pairList!=null){
                        recordDto.setPairList(pairList);
                    }
                    // 配对结算金额
                    String diffAmountValue = recordEle.attributeValue(ATTR_DIFF_AMOUNT);
                    if(StringUtils.isNotBlank(diffAmountValue)){
                        recordDto.setDiffAmount(Double.valueOf(diffAmountValue));
                    }

                    BillUtils.addRecordToSummary(summaryDto, recordDto, null);
                }
            }
            return summaryDto;
        }catch(DocumentException e){
            log.error("", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveStockSummary(StockSummaryDto summaryDto) {
        DocumentFactory factory = DocumentFactory.getInstance();

        Document document = factory.createDocument();
        document.setXMLEncoding("UTF8");

        Element rootEle = factory.createElement(ELE_ROOT);
        rootEle.addAttribute(ATTR_STOCK_CODE, summaryDto.getStockCode());
        rootEle.addAttribute(ATTR_STOCK_NAME, summaryDto.getStockName());
        if(summaryDto.getDiffAmount()!=null){
            rootEle.addAttribute(ATTR_DIFF_AMOUNT, summaryDto.getDiffAmount().toString());
        }
        if(summaryDto.getFeeService()!=null){
            rootEle.addAttribute(ATTR_FEE_SERVICE, summaryDto.getFeeService().toString());
        }
        if(summaryDto.getFeeStamp()!=null){
            rootEle.addAttribute(ATTR_FEE_STAMP, summaryDto.getFeeStamp().toString());
        }
        if(summaryDto.getAfterNumber()!=null){
            rootEle.addAttribute(ATTR_AFTER_NUMBER, summaryDto.getAfterNumber().toString());
        }
        document.setRootElement(rootEle);

        for(StockRecordDto recordDto : summaryDto.getRecordList()){
            Element recordEle = factory.createElement(ELE_RECORD);
            recordEle.addAttribute(ATTR_DATE, recordDto.getDate());
            recordEle.addAttribute(ATTR_TIME, recordDto.getTime());
            recordEle.addAttribute(ATTR_BUSINESS_NAME, recordDto.getBusinessName().getName());
            recordEle.addAttribute(ATTR_STOCK_NAME, recordDto.getStockName());
            recordEle.addAttribute(ATTR_STOCK_CODE, recordDto.getStockCode());
            recordEle.addAttribute(ATTR_TRADE_PRICE, recordDto.getTradePrice().toString());
            recordEle.addAttribute(ATTR_TRADE_NUMBER, recordDto.getTradeNumber().toString());
            recordEle.addAttribute(ATTR_TRADE_AMOUNT, recordDto.getTradeAmount().toString());
            recordEle.addAttribute(ATTR_AFTER_NUMBER, recordDto.getAfterNumber().toString());
            recordEle.addAttribute(ATTR_FEE_SERVICE, recordDto.getFeeService().toString());
            recordEle.addAttribute(ATTR_FEE_STAMP, recordDto.getFeeStamp().toString());
            recordEle.addAttribute(ATTR_FEE_TRANSFER, recordDto.getFeeTransfer().toString());
            recordEle.addAttribute(ATTR_FEE_EXTRA, recordDto.getFeeExtra().toString());
            recordEle.addAttribute(ATTR_FEE_CLEAR, recordDto.getFeeClear().toString());
            recordEle.addAttribute(ATTR_CLEAR_AMOUNT, recordDto.getClearAmount().toString());
            recordEle.addAttribute(ATTR_AFTER_AMOUNT, recordDto.getAfterAmount().toString());
            recordEle.addAttribute(ATTR_ENTRUST_CODE, recordDto.getEntrustCode());

            String pairAttr = this.makePairAttr(recordDto.getPairList(), recordDto.getTradeNumber());
            if(pairAttr!=null){
                recordEle.addAttribute(ATTR_PAIR, pairAttr);
            }
            if(recordDto.getDiffAmount()!=null){
                recordEle.addAttribute(ATTR_DIFF_AMOUNT, recordDto.getDiffAmount().toString());
            }
            rootEle.add(recordEle);
        }

        FileOutputStream output = null;
        try{
            File storeFolder = this.getStoreFile();
            String xmlFileName = "bill_" + summaryDto.getStockCode() + ".xml";
            File xmlFile = new File(storeFolder, xmlFileName);
            log.info("保存股票交易记录, file={}", xmlFile.getAbsolutePath());
            output = new FileOutputStream(xmlFile);
            OutputFormat format = new OutputFormat("  ", true, "UTF8");
            XMLWriter writer = new XMLWriter(output, format);
            writer.write(document);
            writer.close();
        }catch(IOException e){
            throw new RuntimeException(e);
        }finally {
            IOUtils.closeQuietly(output);
        }
    }

    @Override
    public List<StockSummaryDto> loadStockList() {
        List<StockSummaryDto> stockList = new ArrayList<>();
        File storeFolder = this.getStoreFile();
        for(File xmlFile : storeFolder.listFiles()){
            if(xmlFile.getName().endsWith(".xml")){
                log.info("加载股票账单记录：" + xmlFile.getAbsolutePath());
                StockSummaryDto summaryDto = this.loadXmlFile(xmlFile);
                stockList.add(summaryDto);
            }
        }
        return stockList;
    }
}
